package com.jolink.omiis.wechat.inner;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jolink.omiis.wechat.api.impl.WeChatApiBaseService;
import com.jolink.omiis.wechat.keys.IPreferences;
import com.jolink.omiis.wechat.message.Encryption;
import com.jolink.omiis.wechat.util.DateUtil;
import com.jolink.omiis.wechat.util.WeChatConfiguration;

import io.socket.client.Ack;
import io.socket.client.IO;
import io.socket.client.IO.Options;
import io.socket.client.Socket;
import io.socket.emitter.Emitter.Listener;

public abstract class AbstractOmiisMessager extends WeChatApiBaseService implements IOmiisMessager {
	private static Logger logger = LoggerFactory.getLogger(AbstractOmiisMessager.class);
	private static Socket socket;
	private Options opts;
	private String uri = "";
	private String host = "localhost";
	private int port = 7005;
	private String userId = "omiis-wechat";
	private ResponseMessageHandler rmh;
	private String channelId = "WECHAT";
	private String senderId = channelId;
	private String senderRole = "CUSTOMER";
	private String role = IPreferences.CHANNEL_ID;
	private String reigster_msg_type = "REGISTER";
	private volatile static boolean isConnected = false;
	/**
	 * 与bus断开连接，消息发送失败，需要缓存，待连接修复重新发送，
	 */
	private static List<JSONObject> cacheBusMessages = Collections.synchronizedList(new ArrayList<>());

	@Override
	public synchronized void registerMessageListener(ResponseMessageHandler rmh) {
		this.rmh = rmh;
		if (isConnected()) {
			throw new RuntimeException("已连接全媒体Server！");
		}
		try {
			config();

			socket = IO.socket(uri, opts);
			registerDefaultEvent();
			socket.on(this.rmh.getEvent(), this.rmh);
			connect();
		} catch (URISyntaxException e) {
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	/**
	 * 连接至服务器之后，需要注册，暂时内部注册，后续是否开放视情况定夺 by 9527
	 */
	private synchronized void registerDefaultEvent() {
		socket.on(socket.EVENT_CONNECT, new Listener() {
			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_CONNECT", Arrays.toString(args));
				logger.info("已连接至全媒体Server，申请注册。");
				BusMessage busMessage = new BusMessage();
				busMessage.setChannelId(channelId);
				busMessage.setMsgType(reigster_msg_type);
				busMessage.setSenderId(senderId);
				busMessage.setSenderRole(senderRole);
				socket.emit(EVENT_OMIIS_CUSTOM_MESSAGE, new Object[] { new JSONObject(busMessage) }, new Ack() {
					@Override
					public void call(Object... args) {
						logger.info("Emit返回信息：{}。", Arrays.toString(args));
						isConnected = true;
					}
				});
			}
		}).on(socket.EVENT_DISCONNECT, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_DISCONNECT", Arrays.toString(args));
				isConnected = false;
			}
		}).on(socket.EVENT_RECONNECT, new Listener() {

			@Override
			public void call(Object... args) {
				isConnected = true;
				logger.info("Event：{}，Msg：{}.", "EVENT_RECONNECT", Arrays.toString(args));
			}
		}).on(socket.EVENT_CONNECT_ERROR, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_CONNECT_ERROR", Arrays.toString(args));
				isConnected = false;
			}
		}).on(socket.EVENT_CONNECT_TIMEOUT, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_CONNECT_TIMEOUT", Arrays.toString(args));
				isConnected = false;
			}
		}).on(socket.EVENT_RECONNECT_FAILED, new Listener() {

			@Override
			public void call(Object... args) {
				isConnected = false;
				logger.info("Event：{}，Msg：{}.", "EVENT_RECONNECT_FAILED", Arrays.toString(args));
			}
		}).on(socket.EVENT_RECONNECT_ERROR, new Listener() {

			@Override
			public void call(Object... args) {
				isConnected = false;
				logger.info("Event：{}，Msg：{}.", "EVENT_RECONNECT_ERROR", Arrays.toString(args));
			}
		}).on(socket.EVENT_MESSAGE, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_MESSAGE", Arrays.toString(args));
			}
		}).on(socket.EVENT_RECONNECTING, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_RECONNECTING", Arrays.toString(args));
			}
		}).on(socket.EVENT_ERROR, new Listener() {

			@Override
			public void call(Object... args) {
				logger.info("Event：{}，Msg：{}.", "EVENT_ERROR", Arrays.toString(args));
			}
		});
	}

	@Override
	public void connect() {
		if (isConnected()) {
			throw new RuntimeException("已连接全媒体Server！");
		}
		if (rmh == null) {
			throw new RuntimeException("未注册消息Listener！");
		}
		try {
			socket = socket.connect();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	@SuppressWarnings("static-access")
	private void config() {
		WeChatConfiguration conf = rmh.getContext().getServiceUtil().getConfiguration();
		host = conf.getString("OMIIS_SERVER_HOST", "localhost");
		port = conf.getInt("OMIIS_SERVER_PORT", 7005);
		userId = conf.getString("OMIIS_SERVER_USER_ID", "omiis-wechat");
		String param = "role=WECHAT&userId=omiis-wechat&password=omiis-wechat";
		String token = com.jolink.omiis.wechat.http.HttpRequest
				.sendHttpPostRequest("http://" + host + ":7003/token/request", param);
		boolean is_exit_on_connect_bus_error = WeChatConfiguration.getIngleton()
				.getBoolean("OMIIS_IS_EXIT_ON_CONNECT_BUS_ERROR");
		if (StringUtils.isEmpty(token) && is_exit_on_connect_bus_error) {
			throw new RuntimeException("获取全媒体连接Token失败！");
		}
		// uri = "http://" + host + ":" + port + "?userId=" + userId;
		uri = "http://" + host + ":" + port + "?userId=" + userId + "&token=" + token + "&role=" + role;
		logger.info("连接全媒体Token：{}，全媒体服务地址：{}。", token, uri);
		Options options = new Options();
		options.forceNew = true;
		options.port = port;
		options.reconnection = true;
		options.upgrade = true;
		options.timeout = 5000;
		options.rememberUpgrade = true;
	}

	@Override
	public boolean isConnected() {
		return socket != null && socket.connected();
	}

	@Override
	public synchronized <T> String sendMessage(T message) {
		if (!isConnected()) {
			logger.warn("微信与全媒体通讯通道已断开，重新连接！");
			connect();
		}
		if (!isConnected()) {
			try {
				int tryCounter = 0;
				int limit = 5;
				do {
					Thread.currentThread().sleep(100);
					tryCounter++;
					logger.info("与全媒体通道断开，重试第：{}次。", (tryCounter));
				} while (!isConnected() && tryCounter <= limit);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
		}

		Map<String, String> map = null;
		if (message instanceof Map) {
			map = (Map) message;
		} else {
			throw new RuntimeException("不支持的消息类型！");
		}

		BusMessage bm = new BusMessage();
		// bm.setSenderId(map.get("senderId"));
		bm.setSenderName(map.get("sender_name"));
		bm.setMsgType(map.get("msgType"));
		bm.setChannelId("WECHAT");
		bm.setConversationId(map.get("conversationId"));
		if (map.containsKey("senderRole")) {
			bm.setSenderRole(map.get("senderRole"));
			String agentIds = map.get("receiverId");
			String[] agentList = agentIds.split(",");
			logger.info("此处应该是坐席：{}，坐席列表：{}。", map, agentList);
			bm.setSenderId(agentList[0]);
			bm.setReceiverId(map.get("user_id"));
		} else {
			bm.setSenderId(map.get("user_id"));
			bm.setReceiverId(map.get("receiverId"));
			bm.setSenderRole(senderRole);
		}
		bm.setSendTime(DateUtil.getCurrentDate());
		bm.setMsgContent(map.get("msgContent"));
		// 20161227 update by wangyq
		bm.setCustomerName(bm.getSenderName());
		bm.setCustomerId(bm.getSenderId());
		bm.setCustomerOpenId(map.get("open_id"));
		JSONObject jsonObj = null;
		long t1 = System.currentTimeMillis();
		if (!cacheBusMessages.isEmpty()) {
			logger.info("有待发送的缓存信息，信息数量：{}。", cacheBusMessages.size());
			for (JSONObject cacheBM : cacheBusMessages) {
				// cacheBusMessages.remove(cacheBM);
				send(cacheBM);
			}
			cacheBusMessages.clear();
		}
		jsonObj = new JSONObject(bm);
		send(jsonObj);
		long t2 = System.currentTimeMillis();
		logger.info("微信用户：{}，userId：{}，接收用户：{}，发送信息：{}，至全媒体结束，耗时：{}毫秒。", map.get("open_id"), bm.getSenderId(),
				bm.getReceiverId(), jsonObj, (t2 - t1));
		return "true";
	}

	private void send(JSONObject jsonObj) {
		if (!isConnected()) {
			cacheBusMessages.add(jsonObj);
			return;
		}
		Encryption encryption = new Encryption();
		String encryptMessage = encryption.encrypt(jsonObj.toString(), KEY_1, KEY_2, KEY_3);
		socket.emit(EVENT_OMIIS_CUSTOM_MESSAGE, new Object[] { encryptMessage }, new Ack() {
			@Override
			public void call(Object... args) {
				logger.info("Emit返回的信息：{}。", Arrays.toString(args));
			}
		});
		// socket.emit(EVENT_OMIIS_CUSTOM_MESSAGE, new Object[] { jsonObj }, new
		// Ack() {
		// @Override
		// public void call(Object... args) {
		// logger.info("Emit返回的信息：{}。", Arrays.toString(args));
		// }
		// });
	}

	@Override
	public void disconnect() {
		if (socket == null || !socket.connected()) {
			logger.error("微信开放平台与全媒体Server通讯通道已关闭。");
			return;
		}
		try {
			socket.disconnect();
			isConnected = false;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
}
